
using System.Drawing;

using Bee.Base.Abstractions;
using Bee.Base.Abstractions.Tasks;
using Bee.Base.Models.Tasks;
using Bee.Plugin.Multimedia.Models;

using FFMpegCore;

using Ke.Bee.Localization.Localizer.Abstractions;

using LanguageExt;

namespace Bee.Plugin.Multimedia.Tasks;



public class VideoSnapshotTaskHandler(ICoverHandler coverHandler, ILocalizer localizer) :
    TaskHandlerBase<VideoSnapshotArguments>(coverHandler)
{
    private readonly ILocalizer _l = localizer;

    public override async Task<Fin<Unit>> ExecuteAsync(TaskItem taskItem,
        VideoSnapshotArguments arguments,
        Action<double> progressCallback,
        CancellationToken cancellationToken = default)
    {
        var fileName = Path.GetFileNameWithoutExtension(taskItem.Input);

        var times = taskItem.CustomArguments?.Split(MultimediaProcessConsts.DefaultDelimiter).Distinct();
        if (times == null)
        {
            return Fin<Unit>.Fail(string.Format(_l["Task.TaskItem.Arguments.Empty"], fileName));
        }

        // 分析原视频信息
        var analysis = FFProbe.Analyse(taskItem.Input);
        var video = analysis.VideoStreams.FirstOrDefault();
        var timeList = ParseTimeList(times);
        int processedCount = 0; // 已处理时间戳数量

        var size = new Size(
            arguments.Width ?? video?.Width ?? -1,
            arguments.Height ?? video?.Height ?? -1)
            ;

        foreach (var time in timeList)
        {
            // 输出文件
            var outputFile = Path.Combine(arguments.OutputDirectory,
                $"{fileName}.{time.TimeSpan.TotalSeconds}.{arguments.OutputFormat}")
                ;

            // 00:00:01,3|00:00:05,5|00:00:15,2
            switch (arguments.OutputFormat)
            {
                case "png":
                    await FFMpeg.SnapshotAsync(taskItem.Input, outputFile, size, time.TimeSpan);
                    break;
                case "gif":
                    await FFMpeg.GifSnapshotAsync(taskItem.Input, outputFile, size, time.TimeSpan,
                        time.TimeSpan.Add(new TimeSpan(time.Duration)))
                        ;
                    break;
            }

            processedCount++;
            progressCallback((int)(processedCount / (double)timeList.Count * 100));
        }

        return Fin<Unit>.Succ(Unit.Default);
    }

    private List<TimeTuple> ParseTimeList(IEnumerable<string> times)
    {
        List<TimeTuple> list = [];
        foreach (var time in times)
        {
            if (time.IndexOf(',') > -1)
            {
                var arr = time.Split(',');
                if (TimeSpan.TryParse(arr[0], out TimeSpan ts))
                {
                    list.Add(new TimeTuple(ts, int.TryParse(arr[1], out int duration) ? duration : 0));
                }
            }
            else
            {
                if (TimeSpan.TryParse(time, out TimeSpan ts))
                {
                    list.Add(new TimeTuple(ts));
                }
            }
        }
        return list;
    }

    internal class TimeTuple(TimeSpan ts, int duration = 0)
    {
        public TimeSpan TimeSpan { get; set; } = ts;
        public int Duration { get; set; } = duration;
    }
}
